<!DOCTYPE html>
<html lang="zh">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BallWar</title>
  <link rel="stylesheet" type="text/css" href="index.css">
  </link>
  <style>
    body {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
  </style>
</head>

<body>
  <div class="run_area">
    <canvas class="canvas"></canvas>
  </div>
  <button id="start">start</button>
  <button id="pause">pause</button>
  <script src="matter.js"></script>
  <script>
    // 配置常量
    const CONFIG = {
      mainSize: 500,
      gridCount: 10,
      ballCount: 4,// 球的数量不超过 9   
      baseColor: "#eee",
      ballColors: ['#FF0000', '#00FF00', '#0000FF', '#FF00FF', '#FFFF00', '#00FFFF', '#FFFFFF', '#808080', '#FFA500'],
      categories: {
        BALL: 2048,
        GRID: 1024
      }
    };

    // 工具函数
    const Utils = {
      randomVelocity() {
        const magnitude = Math.random() * 5 + 5;
        const angle = Math.random() * Math.PI * 2;
        return {
          x: magnitude * Math.cos(angle),
          y: magnitude * Math.sin(angle)
        };
      },
      getRandomElements(array, n) {
        const shuffled = [...array];
        const result = [];
        for (let i = 0; i < n; i++) {
          const randomIndex = Math.floor(Math.random() * shuffled.length);
          result.push(shuffled[randomIndex]);
          shuffled.splice(randomIndex, 1);
        }
        return result;
      },
      createStartPositions(size, count) { // 创建初始位置
        const positions = [];
        const divisions = Math.ceil(Math.sqrt(count));
        const blockSize = size / divisions;
        for (let i = 0; i < divisions; i++) {
          for (let j = 0; j < divisions; j++) {
            positions.push({
              x: blockSize * (i + 0.5),
              y: blockSize * (j + 0.5)
            });
          }
        }
        return Utils.getRandomElements(positions,count);
      }

    };

    // 场景初始化
    class SceneManager {
      constructor() {
        const { mainSize, gridCount, categories } = CONFIG;
        this.run_area = document.querySelector('.run_area');
        this.showArea = document.createElement("div");
        this.showArea.classList.add("show_area");
        this.canvas = document.querySelector('.canvas');
        this.baseSize = mainSize / gridCount;
        this.engine = this.createEngine();
        this.render = this.createRenderer();
        this.walls = this.createWalls();
        this.grids = this.createGrids();
        this.balls = this.createBalls();
        this.mouseConstraint = this.createMouseConstraint();
        this.setupWorld();
        this.setupEventListeners();
      }

      createEngine() {
        return Matter.Engine.create({ gravity: { x: 0, y: 0 } });
      }

      createRenderer() {
        const { mainSize } = CONFIG;
        const render = Matter.Render.create({
          element: this.run_area,
          engine: this.engine,
          canvas: this.canvas,
          options: {
            width: mainSize,
            height: mainSize,
            wireframes: false
          }
        });
        Matter.Render.lookAt(render, {
          min: { x: 0, y: 0 },
          max: { x: mainSize, y: mainSize }
        });
        return render;
      }

      createWalls() { // 创建边界墙
        const { mainSize, categories } = CONFIG;
        const wallSize = mainSize;
        const wallOpt = {
          collisionFilter: { category: categories.BALL },
          isStatic: true,
          friction: 0,
          render: { visible: false }
        };

        return [
          Matter.Bodies.rectangle(mainSize / 2, -wallSize / 2, mainSize, wallSize, wallOpt),
          Matter.Bodies.rectangle(-wallSize / 2, mainSize / 2, wallSize, mainSize, wallOpt),
          Matter.Bodies.rectangle(mainSize / 2, mainSize + wallSize / 2, mainSize, wallSize, wallOpt),
          Matter.Bodies.rectangle(mainSize + wallSize / 2, mainSize / 2, wallSize, mainSize, wallOpt)
        ];
      }

      createGrids() { // 创建网格
        const { mainSize, gridCount, baseColor } = CONFIG;
        const baseSize = mainSize / gridCount;
        const grids = [];

        for (let i = 0; i < gridCount; i++) {
          for (let j = 0; j < gridCount; j++) {
            const grid = Matter.Bodies.rectangle(
              baseSize * i + baseSize / 2,
              baseSize * j + baseSize / 2,
              baseSize,
              baseSize,
              {
                isStatic: true,
                render: {
                  fillStyle: baseColor,
                  strokeStyle: 'rgba(0,0,0,0.3)',
                  lineWidth: 1
                },
                collisionFilter: {
                  category: CONFIG.categories.GRID,
                },
              }
            );
            grids.push(grid);
          }
        }
        return grids;
      }

      createBalls() {  // 创建 球
        const { ballCount, ballColors, categories } = CONFIG;
        const startPositions = Utils.createStartPositions(CONFIG.mainSize, ballCount);
        const balls = [];

        for (let i = 0; i < ballCount; i++) {
          const mask = this.calculateBallMask(i, ballCount);
          const ball = this.createSingleBall(startPositions[i], ballColors[i], mask);
          balls.push(ball);
        }
        return balls;
      }

      calculateBallMask(currentIndex, totalBalls) { // 计算球的碰撞掩码
        const { categories } = CONFIG;
        let mask = categories.BALL;
        for (let j = 0; j < totalBalls; j++) {
          if (currentIndex !== j) {
            mask |= Math.pow(2, j);
          }
        }
        return mask;
      }

      createSingleBall(position, color, mask) { // 创建单个球
        const ball = Matter.Bodies.circle(position.x, position.y, this.baseSize / 2 - this.baseSize * 0.1, {
          restitution: 1,
          friction: -0.05,
          frictionAir: -0.001,
          collisionFilter: {
            category: CONFIG.categories.BALL,
            mask: mask
          },
          render: {
            strokeStyle: '#666',
            lineWidth: this.baseSize * 0.1,
            fillStyle: color
          }
        });
        Matter.Body.setVelocity(ball, Utils.randomVelocity());
        return ball;
      }

      createMouseConstraint() { // 创建鼠标约束
        const mouse = Matter.Mouse.create(this.render.canvas);
        const mouseConstraint = Matter.MouseConstraint.create(this.engine, {
          mouse: mouse,
          constraint: {
            stiffness: 1,
            render: {
              visible: false,
            },
            collisionFilter: {
              category: CONFIG.categories.BALL,
            }
          }
        });
        this.render.mouse = mouse;
        return mouseConstraint;
      }

      setupWorld() {
        Matter.World.add(this.engine.world, [
          ...this.walls,
          ...this.grids,
          ...this.balls,
          this.mouseConstraint,
        ]);
      }

      setupEventListeners() {
        document.body.addEventListener('touchmove', (e) => {
          e.preventDefault();
        }, { passive: false });
      }

      updateScene() {
        if (!this.run) return
        requestAnimationFrame(() => this.updateScene());
        Matter.Engine.update(this.engine, 1000 / 60);
        this.balls.forEach(ball => {
          const gridUnderneath = Matter.Query.collides(ball, this.grids);
          gridUnderneath.forEach(item => {
            const grid = item.bodyA;
            grid.render.fillStyle = ball.render.fillStyle;
            grid.collisionFilter.category = Math.pow(2, CONFIG.ballColors.indexOf(ball.render.fillStyle));
          });
        });
        this.takeShowArea()
        Matter.Render.world(this.render);
      }
      takeShowArea() { // 显示数据
        this.showArea.innerHTML = ""
        this.run_area.appendChild(this.showArea)
        let outer = document.createElement('div')
        outer.classList.add('show_area')
        let maxCount = 0
        let maxIndex = 0
        let GridCount = this.getGridCount()
        GridCount.forEach((item, index) => {
          if (item.count > maxCount) {
            maxCount = item.count;
            maxIndex = index;
          }
        });
        for (let i = 0; i < CONFIG.ballCount + 1; i++) {
          let one = document.createElement('div')
          one.classList.add('show_area_item')

          let color = document.createElement('div')
          color.classList.add('show_area_item_color')
          color.style.backgroundColor = GridCount[i].color

          let count = document.createElement('div')
          count.classList.add('show_area_item_count')
          if (parseInt(maxIndex) === i) {
            count.classList.add('show_area_item_count_max')
          }
          count.innerText = GridCount[i].count
          one.appendChild(color)
          one.appendChild(count)
          outer.appendChild(one)
        }
        this.showArea.appendChild(outer)
      }
      getGridCount() { // 获取各色网格数量
        const allColors = [CONFIG.baseColor, ...CONFIG.ballColors];
        return allColors.map((color, index) => {
          return {
            color: color,
            count: this.grids.reduce((count, grid) => count + (grid.render.fillStyle === color ? 1 : 0), 0)
          };
        });
      }
      start() {
        this.run = true;
        this.updateScene();
      }
      pause() {
        this.run = false;
      }
    }

    // 初始化并启动场景
    const scene = new SceneManager();
    scene.start();
    scene.pause();
    document.querySelector('#pause').addEventListener('click', () => {
      scene.pause();
    })
    document.querySelector('#start').addEventListener('click', () => {
      scene.start();
    })
  </script>
</body>

</html>